package com.flydog.taskManager.service.impl;

import cn.hutool.core.util.StrUtil;
import com.flydog.taskManager.dto.AdminUserDetails;
import com.flydog.taskManager.dto.AdminUserParam;
import com.flydog.taskManager.dto.UpdateAdminPasswordParam;
import com.flydog.taskManager.repository.*;
import com.flydog.taskManager.security.util.JwtTokenUtil;
import com.flydog.taskManager.service.AdminUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AdminUserServiceImpl implements AdminUserService {

    @Autowired
    private AdminUserRepository userRepository;

    @Autowired
    private AdminRoleRepository roleRepository;

    @Autowired
    private AdminPermissionRepository permissionRepository;

    @Autowired
    private UserRoleRelationRepository userRoleRelationRepository;

    @Autowired
    private RolePermissionRelationRepository rolePermissionRelationRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Override
    public Optional<AdminUser> getUserByName(String name) {
        return userRepository.findByName(name);
    }


    @Override
    public AdminUser register(AdminUserParam param) {
        AdminUser user = new AdminUser();
        BeanUtils.copyProperties(param, user);
        user.setCreateTime(Calendar.getInstance().getTime());
        user.setStatus(1);
        String encodedPassword = passwordEncoder.encode(param.getPassword());
        user.setPassword(encodedPassword);
        AdminRole role = roleRepository.getOne(param.getRoleId());
        int count = role.getAdminCount() + 1;
        role.setAdminCount(count);
        roleRepository.save(role);
        AdminUser savedUser = userRepository.save(user);
        UserRoleRelation userRoleRelation = new UserRoleRelation();
        userRoleRelation.setUserId(savedUser.getId());
        userRoleRelation.setRoleId(param.getRoleId());
        userRoleRelationRepository.save(userRoleRelation);
        return savedUser;
    }


    @Override
    public String login(String name, String password) {
        String token = null;
        UserDetails userDetails = loadUserByUsername(name);
        if (userDetails.isEnabled() && passwordEncoder.matches(password, userDetails.getPassword())) {
            token = jwtTokenUtil.generateToken(userDetails);
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null);
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            Optional<AdminUser> optionalAdminUser = userRepository.findByName(name);
            optionalAdminUser.ifPresent(
                    user -> {
                        user.setLoginTime(Calendar.getInstance().getTime());
                        userRepository.save(user);
                    }
            );
        }
        return token;
    }

    @Override
    public Page<AdminUser> list(String keyword, Integer pageSize, Integer pageNum) {
        return userRepository.findByNameLike(keyword,
                PageRequest.of(pageNum - 1, pageSize, Sort.by(Sort.Direction.DESC, "createTime")));
    }

    @Override
    public Optional<AdminUser> getUser(Long id) {
        return userRepository.findById(id);
    }

    @Override
    public AdminUser delete(Long id) {
        Optional<AdminUser> userOptional = getUser(id);
        if (userOptional.isPresent()) {
            userRepository.deleteById(id);
            return userOptional.get();
        }
        return null;
    }

    @Override
    public AdminUser update(Long id, AdminUserParam param) {
        Optional<AdminUser> optionalAdminUser = getUser(id);
        if (optionalAdminUser.isPresent()) {
            AdminUser user = optionalAdminUser.get();
            BeanUtils.copyProperties(param, user);
            return userRepository.save(user);
        }
        return null;
    }

    @Override
    public List<AdminRole> getRoleList() {
        return roleRepository.findAll(Sort.by("name"));
    }

    @Override
    public List<AdminRole> getRoleList(Long adminId) {
        return userRoleRelationRepository.findByUserId(adminId).stream().map(
                relation -> roleRepository.findById(relation.getRoleId()).orElse(null)
        ).collect(Collectors.toList());
    }

    @Override
    public List<AdminPermission> getPermissionList() {
        return permissionRepository.findAll(Sort.by("name"));
    }

    @Override
    public List<AdminPermission> getPermissionList(Long roleId) {
        return rolePermissionRelationRepository.findByRoleId(roleId).stream().map(
                permission -> permissionRepository.findById(permission.getId()).orElse(null)
        ).collect(Collectors.toList());
    }


    @Override
    public int updatePassword(UpdateAdminPasswordParam param) {
        if (StrUtil.isEmpty(param.getUsername())
                || StrUtil.isEmpty(param.getOldPassword())
                || StrUtil.isEmpty(param.getNewPassword())) {
            return -1;
        }
        Optional<AdminUser> optionalAdminUser = userRepository.findByName(param.getUsername());
        if (optionalAdminUser.isPresent()) {
            AdminUser user = optionalAdminUser.get();
            if (!passwordEncoder.matches(param.getOldPassword(), user.getPassword())) {
                return -3;
            }
            user.setPassword(passwordEncoder.encode(param.getNewPassword()));
            return 1;
        } else {
            return -2;
        }
    }

    @Override
    public int updateRole(Long userId, Long[] roleIds) {
        int count = roleIds == null ? 0 : roleIds.length;
        userRoleRelationRepository.deleteByUserId(userId);
        assert roleIds != null;
        userRoleRelationRepository.saveAll(
                Arrays.stream(roleIds).map(
                        roleId -> {
                            UserRoleRelation relation = new UserRoleRelation();
                            relation.setRoleId(roleId);
                            relation.setUserId(userId);
                            return relation;
                        }
                ).collect(Collectors.toList())
        );
        return count;
    }

    @Override
    public int updatePermission(Long roleId, Long[] permissionIds) {
        int count = permissionIds == null ? 0 : permissionIds.length;
        rolePermissionRelationRepository.deleteByRoleId(roleId);
        assert permissionIds != null;
        rolePermissionRelationRepository.saveAll(
                Arrays.stream(permissionIds).map(
                        permissionId -> {
                            RolePermissionRelation relation = new RolePermissionRelation();
                            relation.setRoleId(roleId);
                            relation.setPermissionId(permissionId);
                            return relation;
                        }
                ).collect(Collectors.toList())
        );
        return count;
    }

    @Override
    public UserDetails loadUserByUsername(String username) {
        Optional<AdminUser> optionalAdminUser = userRepository.findByName(username);
        if (optionalAdminUser.isPresent()) {
            Set<String> permissionList = new HashSet<>();
            Long userId = optionalAdminUser.get().getId();
            List<UserRoleRelation> roleRelations = userRoleRelationRepository.findByUserId(userId);
            roleRelations.stream().map(UserRoleRelation::getRoleId).forEach(
                    roleId -> {
                        Optional<AdminRole> optionalAdminRole = roleRepository.findById(roleId);
                        if (optionalAdminRole.isPresent()) {
                            permissionList.add(optionalAdminRole.get().getName());
                            List<String> permissionNameList = rolePermissionRelationRepository
                                    .findByRoleId(optionalAdminRole.get().getId())
                                    .stream()
                                    .map(RolePermissionRelation::getId)
                                    .map(perMissionId -> permissionRepository.findById(perMissionId))
                                    .map(
                                            optionalPermission ->
                                                    optionalPermission.map(AdminPermission::getName).orElse(null)
                                    )
                                    .collect(Collectors.toList());
                            permissionList.addAll(permissionNameList);
                        }
                    }
            );
            return new AdminUserDetails(optionalAdminUser.get(), permissionList);
        }
        throw new UsernameNotFoundException("用户名或密码错误");
    }
}
